﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharpMap.Geometries;
using System.Diagnostics;
using SharpMap.Layers;
using System.Runtime.InteropServices;
using System.Reflection;

namespace SharpMap.Control
{
    public enum enumMapOperation
    {
        coArrow,
        coZoomIn,
        coZoomOut,
        coPan
    }

    enum enumTracking
    {
        coTrackNothing,
        coTrackRectangle,
        coTrackLine
    }

    public partial class AxSharpMapControl : UserControl
    {
        /// <summary>
        /// Sharp Map Object
        /// </summary>
        private SharpMap.Map _map = new SharpMap.Map();
        private MapViewer _viewer = null;
        /// <summary>
        /// Map Image Cache
        /// </summary>
        private Image _mapImage = null;
        private Rectangle _image_rect = Rectangle.Empty;
        private int _image_offset_x = 0;
        private int _image_offset_y = 0;
        /// <summary>
        /// flag whether the map need be repainted
        /// </summary>
        private bool _repaint = true;
        
        /// <summary>
        /// Map Control Operation
        /// </summary>
        private enumMapOperation _operation = enumMapOperation.coArrow;
        
        #region Tracking Definition
        private enumTracking _tracking = enumTracking.coTrackNothing;
        private RectangleTracker _tracker = null;
        #endregion

        private System.Drawing.Point _mouse_start = new System.Drawing.Point(0, 0);
        private bool _pan_begin = false;

        #region Cursor Icons
        private Cursor _pan_hand_cursor = null;
        private Cursor _pan_drag_hand_cursor = null;
        #endregion


        #region Win32 Function Declaration
        [DllImport("user32.dll")]  
        public static extern IntPtr LoadCursorFromFile(string fileName);  
 
        [DllImport("user32.dll")]  
        public static extern IntPtr SetCursor(IntPtr cursorHandle);  
 
        [DllImport("user32.dll")]  
        public static extern uint DestroyCursor(IntPtr cursorHandle);
        #endregion

        public AxSharpMapControl()
        {
            InitializeComponent();
        }

        private void AxSharpMapControl_Load(object sender, EventArgs e)
        {
            _map.Size = this.ClientSize;
            _viewer = new MapViewer(_map);

            // 初始化MapViewer的ScreenSize
            _viewer.ScreenSize = this.ClientSize;

            _pan_hand_cursor = LoadCursor(@"E:\Research\CSIRO\AHP-2\SharpMapControl\SharpMapControl\Resources\cursor_hand.cur");
            _pan_drag_hand_cursor = LoadCursor(@"E:\Research\CSIRO\AHP-2\SharpMapControl\SharpMapControl\Resources\cursor_hand.cur");

            //this.Cursor = _pan_hand_cursor;
            //this.Cursor = System.Windows.Forms.Cursors.PanEast;
        }

        #region MapControl Event
        private void AxSharpMapControl_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Size client_size = this.ClientSize;

            if (this._mapImage == null || _repaint == true)
            {
                this._mapImage = DrawMap();
                _repaint = false;
                Debug.WriteLine("repaint");
            }
            if (this._mapImage != null)
            {   
                Rectangle image_win = this._viewer.ScreenWindow;
                //Rectangle image_win = this._image_rect;
                Rectangle image_rect = Rectangle.Empty;
                image_rect.X = image_win.Left+_image_offset_x;
                image_rect.Y = image_win.Top+_image_offset_y;
                image_rect.Width = image_win.Width;
                image_rect.Height = image_win.Height;
                g.DrawImage(this._mapImage, image_rect);
            }
        }

        private void AxSharpMapControl_Resize(object sender, EventArgs e)
        {
            RefreshMap();
        }

        private void AxSharpMapControl_ClientSizeChanged(object sender, EventArgs e)
        {
            if (this._mapImage != null)
            {   
                this._viewer.UpdateScreenSize(this.ClientSize);
                //CalImagePaintingScreenRect(this._mapImage, ref _image_rect);
            }
            SetRepaint();
            RefreshMap();
        }

        private void AxSharpMapControl_MouseDoubleClick(object sender, MouseEventArgs e)
        {
        }

        private void AxSharpMapControl_MouseDown(object sender, MouseEventArgs e)
        {
            switch(_operation)
            {
                case enumMapOperation.coPan:
                    {
                        BeginPan();
                        _mouse_start.X = e.X;
                        _mouse_start.Y = e.Y;
                        //_mouse_moving_1 = _mouse_start;
                    }
                    break;
            }
            switch (_tracking)
            {
                case enumTracking.coTrackRectangle:
                    {
                        _tracker = new RectangleTracker(this);
                        _tracker.MouseDown(e.X, e.Y);
                    }
                    break;
                case enumTracking.coTrackLine:
                    break;;
            }
        }

        private void AxSharpMapControl_MouseMove(object sender, MouseEventArgs e)
        {
            switch (_operation)
            {
                case enumMapOperation.coPan:
                    {
                        if (_pan_begin)
                        {
                            //this.Cursor = _pan_drag_hand_cursor;
                            
                            _image_offset_x = e.X - _mouse_start.X;
                            _image_offset_y = e.Y - _mouse_start.Y;
                            RefreshMap();
                        }
                    }
                    break;
            }
            switch (_tracking)
            {
                case enumTracking.coTrackRectangle:
                    {
                        if (_tracker != null)
                        {
                            _tracker.MouseMoving(e.X, e.Y);
                        }                        
                    }
                    break;
                case enumTracking.coTrackLine:
                    break; ;
            }
        }

        private void AxSharpMapControl_MouseUp(object sender, MouseEventArgs e)
        {
            switch (_operation)
            {
                case enumMapOperation.coPan:
                    if (_pan_begin)
                    {
                        EndPan();
                        
                        // Update Map Bounding Box
                        UpdateMapRect(_mouse_start, new System.Drawing.Point(e.X, e.Y));                        
                        SetRepaint();
                        Invalidate();
                    }
                    //_operation = enumMapOperation.coArrow;
                    break;
            }
            switch (_tracking)
            {
                case enumTracking.coTrackRectangle:
                    {
                        if (_tracker!=null)
                        {
                            _tracker.MouseUp(e.X, e.Y);                            
                            Rectangle rect = _tracker.Rect;
                            _tracker = null;
                            BoundingBox box = RectangleScreenToMap(rect);
                            ZoomToBox(box);
                        }
                    }
                    break;
                case enumTracking.coTrackLine:
                    break; ;
            }
        }

        private void AxSharpMapControl_SizeChanged(object sender, EventArgs e)
        {
            //// SharpMapControl的Size发生变化，更新MapViewer的ScreenSize
            if (this._viewer != null)
            {
                this._viewer.ScreenSize = this.ClientSize;
                //this._viewer.UpdateScreenSize(this.ClientSize);
                //this.
                //this._viewer.Update();
                //if (this._mapImage != null)
                //{
                //    CalImagePaintingScreenRect(this._mapImage, ref _image_rect);
                //}
            }
        }

#endregion

        #region Property
        public Map Map
        {
            get { return this._map;  }
        }
        #endregion

        #region Layers
        public bool AddLayer(SharpMap.Layers.Layer layer)
        {
            if (layer == null)
            {
                return false;
            }
            this._map.Layers.Add(layer);
            UpdateViewerMapExtent();
            return true;
        }
        #endregion

        #region MapView
        public void ZoomIn()
        {
            _operation = enumMapOperation.coZoomIn;
            _tracking = enumTracking.coTrackRectangle;
        }

        public void ZoomOut()
        {
            _operation = enumMapOperation.coZoomOut;
            //_tracking = enumTracking.coTrackRectangle;
            Rectangle rect = this.ClientRectangle;
            Rectangle rect_inflate = InflateRect(rect, 1.5f);
            BoundingBox mapWindow = RectangleScreenToMap(rect_inflate);
            ZoomToBox(mapWindow);
            //this._viewer.UpdateMapWindow(mapWindow);
            //this._map.ZoomToBox(this._viewer.MapWindow);
            //SetRepaint();
        }

        public void Pan()
        {
            _operation = enumMapOperation.coPan ;
            _tracking = enumTracking.coTrackNothing;
        }

        public void FullScreen()
        {
            this._map.ZoomToBox(this._viewer.MapWindow);
        }

        public void ZoomToExtents()
        {
            if (this._map.Layers.Count > 0)
            {
                this._map.ZoomToExtents();
                SetRepaint();
            }
        }

        public void RefreshMap()
        {
            Invalidate();
        }
        #endregion

        #region Methods
        public void TrackLine()
        {
            Tracker tracker = new Tracker(this);
            
            tracker.Dispose();
        }

        public void TrackRectangle()
        {
            _tracking = enumTracking.coTrackRectangle;
        }
        #endregion

        #region Property
        public enumMapOperation Operation
        {
            set { _operation = value;   }
            get {   return _operation;  }
        }
        #endregion

        #region Map View Inner Functions
        private Image DrawMap()
        {
            Image mapImage = null;
            if (this._map.Layers.Count > 0)
            {
                mapImage = this._map.GetMap();
                //mapImage.Save(@"g:\temp\map\map.bmp");
            }

            return mapImage;
        }

        private void SetRepaint()
        {
            this._repaint = true;
        }

        private void ZoomToBox(BoundingBox box)
        {
            this._viewer.UpdateMapWindow(box);
            this._map.ZoomToBox(this._viewer.MapWindow);
            SetRepaint();
            RefreshMap();
        }

        private void UpdateMapRect(System.Drawing.Point start, System.Drawing.Point end)
        {   
            BoundingBox box = this._map.Envelope;
            SharpMap.Geometries.Point sp = this._map.ImageToWorld(new PointF(start.X, start.Y));
            SharpMap.Geometries.Point ep = this._map.ImageToWorld(new PointF(end.X, end.Y));
            this._viewer.MoveMapWindow(sp.X - ep.X, sp.Y - ep.Y);
            ZoomToBox(this._viewer.MapWindow);
        }

        /// <summary>
        /// 添加图层后，更新MapViewer的MapExtent
        /// </summary>
        private void UpdateViewerMapExtent()
        {
            int numLayers = 0;
            numLayers = this._map.Layers.Count;
            if (numLayers == 0)
            {

            }
            else if (numLayers == 1)
            {
                // 只有1个图层时，初始化MapViewer的MapExtent值。
                this._viewer.MapExtent = this._map.Envelope;
                // 更新MapWindow的值
                ILayer layer = this._map.Layers[0];
                this._viewer.UpdateMapWindow(layer.Envelope);
                // 更新ShapMap.Map的Image Size
                this._map.Size = this._viewer.ImageSize;
                this._image_rect = this._viewer.ScreenWindow;
            }
            else
            {
                //当图层大于1时，只更新MapExtent不更新MapWindow
                //地图仍按当前MapWindow显示
                this._viewer.MapExtent = this._map.Envelope;
                this._image_rect = this._viewer.ScreenWindow;
            }
        }

        #endregion

        #region Operation
        private BoundingBox RectangleScreenToMap(Rectangle rect)
        {
            SharpMap.Geometries.Point z = this._map.ImageToWorld(new PointF(0, 0));
            SharpMap.Geometries.Point ul = this._map.ImageToWorld(new PointF(rect.Left, rect.Top));
            SharpMap.Geometries.Point lr = this._map.ImageToWorld(new PointF(rect.Right, rect.Bottom));
            BoundingBox box = new BoundingBox(ul, lr);
            return box;
        }

        private Rectangle InflateRect(Rectangle rect, float factor)
        {
            int width_2 = rect.Width / 2;
            int height_2 = rect.Height / 2;
            int center_x = rect.X + width_2;
            int center_y = rect.Y + width_2;
            int width_f_2 = (int)(width_2 * factor);
            int height_f_2 = (int)(height_2 * factor);

            int left = center_x - width_f_2;
            int top = center_y - height_f_2;
            Rectangle rect_f = Rectangle.Empty;
            rect_f.X = left;
            rect_f.Y = top;
            rect_f.Width = width_f_2 * 2;
            rect_f.Height = height_f_2 * 2;

            return rect_f;
        }

        private void BeginPan()
        {
            _pan_begin = true;
            _image_offset_x = 0;
            _image_offset_y = 0;
        }
        private void EndPan()
        {
            _pan_begin = false;
            _image_offset_x = 0;
            _image_offset_y = 0;
        }
        #endregion

        #region Cursor
        public void SetCursor(Image cursor, System.Drawing.Point hotPoint)
        {
            int hotX = hotPoint.X;
            int hotY = hotPoint.Y;
            Bitmap myNewCursor = new Bitmap(cursor.Width * 2 - hotX, cursor.Height * 2 - hotY);
            Graphics g = Graphics.FromImage(myNewCursor);
            g.Clear(Color.FromArgb(0, 0, 0, 0));
            g.DrawImage(cursor, cursor.Width - hotX, cursor.Height - hotY, cursor.Width,
            cursor.Height);

            //this.Cursor = new Cursor(myNewCursor.GetHicon());

            g.Dispose();
            myNewCursor.Dispose();
        }

        public Cursor LoadCursor(string path)
        {
            Cursor myCursor = new Cursor(Cursor.Current.Handle);
            IntPtr colorCursorHandle = LoadCursorFromFile(path);//鼠标图标路径  
            myCursor.GetType().InvokeMember("handle", BindingFlags.Public |
                                                      BindingFlags.NonPublic | BindingFlags.Instance |
                                                      BindingFlags.SetField, null, myCursor,
                                                     new object[] { colorCursorHandle });
            return myCursor;
        }
        #endregion
    }
}
